<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MQ如何处理的有序的消息 - 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.6;
            background-color: #f9fafb;
        }
        .hero {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .card {
            transition: all 0.3s ease;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            border-radius: 0.5rem;
            overflow: hidden;
        }
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
        }
        .tag {
            display: inline-block;
            padding: 0.25rem 0.75rem;
            border-radius: 9999px;
            font-size: 0.75rem;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.05em;
        }
        .mq-icon {
            font-size: 2rem;
            margin-bottom: 1rem;
            color: #4f46e5;
        }
        .step-number {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 2.5rem;
            height: 2.5rem;
            border-radius: 9999px;
            background-color: #4f46e5;
            color: white;
            font-weight: bold;
            margin-right: 1rem;
        }
        .code-block {
            background-color: #1e293b;
            color: #f8fafc;
            border-radius: 0.5rem;
            padding: 1.5rem;
            font-family: monospace;
            position: relative;
        }
        .code-block::before {
            content: "";
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 0.5rem;
            background: linear-gradient(90deg, #ec4899, #8b5cf6, #6366f1);
            border-top-left-radius: 0.5rem;
            border-top-right-radius: 0.5rem;
        }
        .footer {
            background-color: #1a1a1a;
            color: #e5e7eb;
        }
        .footer a:hover {
            color: #a5b4fc;
        }
    </style>
</head>
<body>
    <!-- Hero Section -->
    <section class="hero py-20 px-4 sm:px-6 lg:px-8">
        <div class="max-w-5xl mx-auto text-center">
            <h1 class="text-4xl md:text-5xl font-bold mb-6">消息队列中的有序消息处理</h1>
            <p class="text-xl md:text-2xl mb-8 opacity-90">探索分布式系统中保证消息顺序性的关键技术与实现方案</p>
            <div class="flex justify-center space-x-4">
                <span class="tag bg-white text-indigo-600">Kafka</span>
                <span class="tag bg-white text-purple-600">RabbitMQ</span>
                <span class="tag bg-white text-rose-600">RocketMQ</span>
                <span class="tag bg-white text-emerald-600">ActiveMQ</span>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
        <!-- Introduction -->
        <section class="mb-16">
            <div class="bg-white card p-6 md:p-8">
                <h2 class="text-2xl md:text-3xl font-bold mb-6 text-gray-800 border-b pb-4">消息队列与有序消息的重要性</h2>
                <div class="grid md:grid-cols-2 gap-8">
                    <div>
                        <p class="mb-4">消息队列在现代分布式系统和微服务架构中扮演着至关重要的角色。它不仅能够解耦系统组件，提升系统的可扩展性和可靠性，还能有效地处理高并发和大数据量的场景。</p>
                        <p class="mb-6">然而，在某些特定的业务场景中，我们不仅需要消息队列来传递消息，还需要保证这些消息的顺序性。</p>
                        <div class="bg-indigo-50 p-4 rounded-lg border border-indigo-100">
                            <h3 class="font-bold text-indigo-800 mb-2">典型应用场景</h3>
                            <ul class="list-disc pl-5 text-indigo-700 space-y-1">
                                <li>电商平台的订单处理流程</li>
                                <li>金融系统的交易指令执行</li>
                                <li>库存管理系统的数据一致性</li>
                            </ul>
                        </div>
                    </div>
                    <div class="flex items-center">
                        <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1721444206628-3d58c47e-4e7e-49bd-9372-4c7a62fabe1a.png" alt="消息队列示意图" class="rounded-lg shadow-md w-full">
                    </div>
                </div>
            </div>
        </section>

        <!-- Ordered Message Concept -->
        <section class="mb-16">
            <div class="bg-white card p-6 md:p-8">
                <h2 class="text-2xl md:text-3xl font-bold mb-6 text-gray-800 border-b pb-4">有序消息的概念与特性</h2>
                
                <div class="grid md:grid-cols-3 gap-6 mb-8">
                    <div class="border-l-4 border-indigo-500 pl-4">
                        <h3 class="font-bold text-lg mb-2">顺序性</h3>
                        <p class="text-gray-600">消息按照特定的顺序发送和处理，对于金融交易、订单处理等场景至关重要。</p>
                    </div>
                    <div class="border-l-4 border-purple-500 pl-4">
                        <h3 class="font-bold text-lg mb-2">一致性</h3>
                        <p class="text-gray-600">通过保持消息顺序，确保系统在不同时间点的数据状态一致。</p>
                    </div>
                    <div class="border-l-4 border-emerald-500 pl-4">
                        <h3 class="font-bold text-lg mb-2">可靠性</h3>
                        <p class="text-gray-600">在高并发、网络延迟和系统故障情况下，消息的顺序不会被打乱。</p>
                    </div>
                </div>

                <div class="mb-8">
                    <h3 class="text-xl font-bold mb-4 text-gray-700">为什么有序消息如此重要？</h3>
                    <div class="grid md:grid-cols-3 gap-4">
                        <div class="bg-gray-50 p-4 rounded-lg">
                            <div class="flex items-center mb-2">
                                <i class="fas fa-check-circle text-green-500 mr-2"></i>
                                <h4 class="font-medium">业务逻辑正确性</h4>
                            </div>
                            <p class="text-sm text-gray-600">确保订单处理、库存管理等业务流程的正确执行。</p>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-lg">
                            <div class="flex items-center mb-2">
                                <i class="fas fa-database text-blue-500 mr-2"></i>
                                <h4 class="font-medium">数据一致性</h4>
                            </div>
                            <p class="text-sm text-gray-600">保证分布式系统中不同服务之间的数据同步。</p>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-lg">
                            <div class="flex items-center mb-2">
                                <i class="fas fa-shield-alt text-yellow-500 mr-2"></i>
                                <h4 class="font-medium">系统可靠性</h4>
                            </div>
                            <p class="text-sm text-gray-600">在故障恢复过程中避免数据丢失或重复处理。</p>
                        </div>
                    </div>
                </div>

                <div>
                    <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1721444315719-07cc3d8d-eeed-4223-a92a-dde51a5edbe7.png" alt="有序消息特性" class="rounded-lg shadow-md w-full">
                </div>
            </div>
        </section>

        <!-- Basic Principles -->
        <section class="mb-16">
            <div class="bg-white card p-6 md:p-8">
                <h2 class="text-2xl md:text-3xl font-bold mb-6 text-gray-800 border-b pb-4">有序消息的基本原理</h2>
                
                <div class="grid md:grid-cols-2 gap-8 mb-8">
                    <div>
                        <div class="flex items-start mb-6">
                            <span class="step-number">1</span>
                            <div>
                                <h3 class="text-xl font-bold mb-2">消息生产</h3>
                                <ul class="list-disc pl-5 space-y-2 text-gray-600">
                                    <li><strong>单一生产者</strong> - 按顺序发送消息</li>
                                    <li><strong>多生产者协调</strong> - 使用全局时间戳或序列号保证顺序</li>
                                </ul>
                            </div>
                        </div>
                        
                        <div class="flex items-start mb-6">
                            <span class="step-number">2</span>
                            <div>
                                <h3 class="text-xl font-bold mb-2">消息传输</h3>
                                <ul class="list-disc pl-5 space-y-2 text-gray-600">
                                    <li><strong>单一队列</strong> - 简单但可能成为瓶颈</li>
                                    <li><strong>分区策略</strong> - 基于关键字段分区保证顺序</li>
                                </ul>
                            </div>
                        </div>
                    </div>
                    
                    <div>
                        <div class="flex items-start mb-6">
                            <span class="step-number">3</span>
                            <div>
                                <h3 class="text-xl font-bold mb-2">消息消费</h3>
                                <ul class="list-disc pl-5 space-y-2 text-gray-600">
                                    <li><strong>单线程消费</strong> - 简单但性能有限</li>
                                    <li><strong>多线程消费协调</strong> - 引入排序逻辑保证顺序</li>
                                </ul>
                            </div>
                        </div>
                        
                        <div class="flex items-start">
                            <span class="step-number">4</span>
                            <div>
                                <h3 class="text-xl font-bold mb-2">容错机制</h3>
                                <ul class="list-disc pl-5 space-y-2 text-gray-600">
                                    <li><strong>消息持久化</strong> - 避免系统故障导致消息丢失</li>
                                    <li><strong>消息重试与重传</strong> - 通过重试队列实现顺序保证</li>
                                    <li><strong>死信队列</strong> - 处理无法成功消费的消息</li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
                
                <div>
                    <img src="https://cdn.nlark.com/yuque/0/2024/png/21449790/1721444418099-f0f8758f-e8a3-493f-8ca8-e7d98fc9e34b.png" alt="有序消息基本原理" class="rounded-lg shadow-md w-full">
                </div>
            </div>
        </section>

        <!-- MQ Comparison -->
        <section class="mb-16">
            <div class="bg-white card p-6 md:p-8">
                <h2 class="text-2xl md:text-3xl font-bold mb-6 text-gray-800 border-b pb-4">不同MQ对有序消息的处理</h2>
                
                <div class="grid md:grid-cols-2 gap-8 mb-8">
                    <div>
                        <div class="flex items-center mb-4">
                            <i class="fas fa-stream mq-icon mr-4"></i>
                            <h3 class="text-xl font-bold">Kafka</h3>
                        </div>
                        <div class="pl-14">
                            <h4 class="font-bold mb-2 text-gray-700">分区机制</h4>
                            <p class="mb-4 text-gray-600">根据消息的键(Key)将消息发送到指定分区，确保同一键的消息进入同一分区。提供默认分区策略，也允许自定义分区器。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">消费者组</h4>
                            <p class="mb-4 text-gray-600">每个分区只能被一个消费者组中的一个消费者消费，从而保证分区内消息的顺序。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">事务机制</h4>
                            <p class="text-gray-600">允许生产者在一个事务中写入多个分区的数据，确保跨分区的消息顺序和一致性。</p>
                        </div>
                    </div>
                    
                    <div>
                        <div class="flex items-center mb-4">
                            <i class="fas fa-rabbit mq-icon mr-4"></i>
                            <h3 class="text-xl font-bold">RabbitMQ</h3>
                        </div>
                        <div class="pl-14">
                            <h4 class="font-bold mb-2 text-gray-700">单队列模式</h4>
                            <p class="mb-4 text-gray-600">将有序消息发送到同一个队列中，消费者按顺序消费队列中的消息。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">分区与交换器</h4>
                            <p class="mb-4 text-gray-600">通过Direct Exchange和Routing Key，将有序消息路由到特定队列。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">Confirm机制</h4>
                            <p class="text-gray-600">生产者发送消息后等待确认，以确保消息已被正确路由并存储。</p>
                        </div>
                    </div>
                </div>
                
                <div class="grid md:grid-cols-2 gap-8">
                    <div>
                        <div class="flex items-center mb-4">
                            <i class="fas fa-rocket mq-icon mr-4"></i>
                            <h3 class="text-xl font-bold">RocketMQ</h3>
                        </div>
                        <div class="pl-14">
                            <h4 class="font-bold mb-2 text-gray-700">队列机制</h4>
                            <p class="mb-4 text-gray-600">生产者根据某个键将消息发送到特定的队列，消费者按队列的顺序消费消息。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">消费者负载均衡</h4>
                            <p class="mb-4 text-gray-600">根据消费者的数量和队列的数量进行分配，每个消费者负责消费若干队列。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">延迟消息</h4>
                            <p class="text-gray-600">通过定时任务来触发消息的消费，确保延迟消息在特定时间点按顺序处理。</p>
                        </div>
                    </div>
                    
                    <div>
                        <div class="flex items-center mb-4">
                            <i class="fas fa-server mq-icon mr-4"></i>
                            <h3 class="text-xl font-bold">ActiveMQ</h3>
                        </div>
                        <div class="pl-14">
                            <h4 class="font-bold mb-2 text-gray-700">Queue和Topic</h4>
                            <p class="mb-4 text-gray-600">通过Queue实现有序消息，消费者按顺序消费队列中的消息。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">消费者优先级</h4>
                            <p class="mb-4 text-gray-600">设置消费者的优先级，在高优先级消费者无法处理消息时，低优先级消费者才会介入。</p>
                            
                            <h4 class="font-bold mb-2 text-gray-700">消息组(Message Group)</h4>
                            <p class="text-gray-600">通过消息头部的组ID将消息分组，确保同一组内的消息按顺序处理。</p>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Partitioning & Sharding -->
        <section class="mb-16">
            <div class="bg-white card p-6 md:p-8">
                <h2 class="text-2xl md:text-3xl font-bold mb-6 text-gray-800 border-b pb-4">分区与分片处理</h2>
                
                <div class="grid md:grid-cols-2 gap-8 mb-8">
                    <div>
                        <h3 class="text-xl font-bold mb-4 text-gray-700">分区(Partitioning)</h3>
                        <p class="mb-4 text-gray-600">将数据集划分为多个更小的、相互独立的子集(分区)，每个分区可以独立地存储和处理数据。</p>
                        
                        <h4 class="font-bold mb-2 text-gray-700">实现方式</h4>
                        <ul class="list-disc pl-5 mb-4 space-y-1 text-gray-600">
                            <li><strong>哈希分区</strong>：根据数据的哈希值分配分区</li>
                            <li><strong>范围分区</strong>：根据数据的值范围分配分区</li>
                            <li><strong>列表分区</strong>：根据数据的具体值列表分配分区</li>
                            <li><strong>组合分区</strong>：结合多种分区策略</li>
                        </ul>
                        
                        <div class="bg-blue-50 p-4 rounded-lg border border-blue-100">
                            <h4 class="font-bold text-blue-800 mb-2">应用场景</h4>
                            <ul class="list-disc pl-5 text-blue-700 space-y-1">
                                <li>Kafka等消息队列系统</li>
                                <li>Cassandra等分布式数据库</li>
                                <li>Hadoop等大数据处理框架</li>
                            </ul>
                        </div>
                    </div>
                    
                    <div>
                        <h3 class="text-xl font-bold mb-4 text-gray-700">分片(Sharding)</h3>
                        <p class="mb-4 text-gray-600">将大规模数据集分割成更小的子集，分布到不同存储节点(分片)上，解决单节点存储和处理能力的限制。</p>
                        
                        <h4 class="font-bold mb-2 text-gray-700">实现方式</h4>
                        <ul class="list-disc pl-5 mb-4 space-y-1 text-gray-600">
                            <li><strong>水平分片</strong>：将表中的行数据分配到不同分片</li>
                            <li><strong>垂直分片</strong>：将表中的列数据分配到不同分片</li>
                            <li><strong>动态分片</strong>：根据数据负载动态调整分片策略</li>
                        </ul>
                        
                        <div class="bg-purple-50 p-4 rounded-lg border border-purple-100">
                            <h4 class="font-bold text-purple-800 mb-2">应用场景</h4>
                            <ul class="list-disc pl-5 text-purple-700 space-y-1">
                                <li>MySQL、MongoDB等数据库</li>
                                <li>Elasticsearch等搜索引擎</li>
                                <li>Redis等分布式缓存</li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Consumer Processing -->
        <section class="mb-16">
            <div class="bg-white card p-6 md:p-8">
                <h2 class="text-2xl md:text-3xl font-bold mb-6 text-gray-800 border-b pb-4">消费者的顺序处理</h2>
                
                <div class="grid md:grid-cols-2 gap-8 mb-8">
                    <div>
                        <h3 class="text-xl font-bold mb-4 text-gray-700">单线程消费</h3>
                        <p class="mb-4 text-gray-600">使用一个单独的线程从消息队列中读取和处理消息，天然保证消息的处理顺序。</p>
                        
                        <div class="bg-gray-100 p-4 rounded-lg mb-4">
                            <div class="flex items-center mb-2">
                                <i class="fas fa-check text-green-500 mr-2"></i>
                                <h4 class="font-medium">优点</h4>
                            </div>
                            <ul class="list-disc pl-5 text-gray-600 space-y-1">
                                <li>顺序保证 - 天然保证消息顺序</li>
                                <li>实现简单 - 不需要复杂并发控制</li>
                            </ul>
                        </div>
                        
                        <div class="bg-gray-100 p-4 rounded-lg">
                            <div class="flex items-center mb-2">
                                <i class="fas fa-exclamation-triangle text-yellow-500 mr-2"></i>
                                <h4 class="font-medium">缺点</h4>
                            </div>
                            <ul class="list-disc pl-5 text-gray-600 space-y-1">
                                <li>性能瓶颈 - 无法充分利用多核CPU</li>
                                <li>延迟增加 - 高并发下处理延迟增加</li>
                            </ul>
                        </div>
                    </div>
                    
                    <div>
                        <h3 class="text-xl font-bold mb-4 text-gray-700">多线程消费</h3>
                        <p class="mb-4 text-gray-600">使用多个线程并发地从消息队列中读取和处理消息，需要采用特定策略保证顺序。</p>
                        
                        <div class="mb-6">
                            <h4 class="font-bold mb-2 text-gray-700">策略和实现</h4>
                            <div class="bg-gray-50 p-4 rounded-lg mb-3">
                                <h5 class="font-medium mb-1">消息分区</h5>
                                <p class="text-sm text-gray-600">按某个键(如用户ID)进行分区，每个分区内的消息顺序保证。</p>
                            </div>
                            <div class="bg-gray-50 p-4 rounded-lg mb-3">
                                <h5 class="font-medium mb-1">消息路由</h5>
                                <p class="text-sm text-gray-600">根据路由信息将消息分配到不同的处理线程。</p>
                            </div>
                            <div class="bg-gray-50 p-4 rounded-lg">
                                <h5 class="font-medium mb-1">顺序队列</h5>
                                <p class="text-sm text-gray-600">消费者端使用顺序队列，由特定线程按顺序处理。</p>
                            </div>
                        </div>
                    </div>
                </div>
                
                <div class="bg-red-50 p-4 rounded-lg border border-red-100">
                    <h3 class="font-bold text-red-800 mb-2">消息无序会导致的问题</h3>
                    <p class="mb-4 text-red-700">例如：发送三个消息(清空，插入，修改)，如果实际顺序变为(插入，修改，清空)，最终结果将是所有数据被清空！</p>
                    
                    <h3 class="font-bold text-red-800 mb-2">解决方案</h3>
                    <p class="text-red-700">在消息上设置pid，消费者先判断pid是否为-1，不是则检查上一个消息是否已消费。未消费则NACK并压入队列。</p>
                </div>
                
                <div class="mt-8">
                    <h3 class="text-xl font-bold mb-4 text-gray-700">代码示例</h3>
                    
                    <div class="grid md:grid-cols-2 gap-6">
                        <div>
                            <h4 class="font-bold mb-2 text-gray-700">消息实体</h4>
                            <div class="code-block">
                                <pre><code class="text-sm">@Data
public class MsgEntity implements Serializable {
    private Integer id;
    private String msg;  // 消息本身
    private Integer pid; // 上一个
    private Boolean isEnd = false;
}</code></pre>
                            </div>
                        </div>
                        
                        <div>
                            <h4 class="font-bold mb-2 text-gray-700">消费者实现</h4>
                            <div class="code-block">
                                <pre><code class="text-sm">@RabbitListener(queues = "sboot-queue-index")
public void listenerSbootQueue(MsgEntity msgEntity, Channel channel, Message message) throws IOException {
    if ("-1".equals(msgEntity.getPid().toString())) {
        System.out.println("消费者:" + msgEntity);
        stringRedisTemplate.opsForValue().set(msgEntity.getId().toString(), "1");
        channel.basicAck(deliveryTag, false);
    } else {
        Integer pid = msgEntity.getPid();
        String val = stringRedisTemplate.opsForValue().get(pid.toString());
        if (val != null && "1".equals(val)) {
            System.out.println("消费者:" + msgEntity);
            stringRedisTemplate.opsForValue().set(msgEntity.getId().toString(), "1");
            channel.basicAck(deliveryTag, false);
        } else {
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}</code></pre>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    </div>

    <!-- Footer -->
    <footer class="footer py-8 px-4 sm:px-6 lg:px-8">
        <div class="max-w-5xl mx-auto">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-xl font-bold mb-1">技术小馆</h3>
                    <p class="text-gray-400">探索技术之美，分享知识之光</p>
                </div>
                <div>
                    <a href="http://www.yuque.com/jtostring" class="text-gray-300 hover:text-white transition-colors duration-300">
                        <i class="fas fa-book-open mr-1"></i> 语雀地址
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>